﻿using System;
using System.Configuration;
using System.Net;
using System.Threading;
using Neoseeker.NAMFox.Tests.Configuration;
using Selenium;
using Neoseeker.NAMFox.Tests.Browsers;

namespace Neoseeker.NAMFox.Tests
{
    public class NAMFoxSelenium : DefaultSelenium
    {
        private CredentialDecryptor credentialDecryptor = new CredentialDecryptor();
        private NetworkCredential login;
        private bool loggedIn;

        public NAMFoxSelenium(ICommandProcessor commandProcessor) : 
            base(commandProcessor)
        {
            this.Configuration = new NAMFoxConfiguration();
            this.Tabs = new BrowserTabCollection();
            this.Toolbar = new NAMFoxToolbar();

            this.TestConfiguration = (NeoseekerSection)ConfigurationManager.GetSection("neoseeker");
            var credentials = this.TestConfiguration.Credentials;

            this.login = new NetworkCredential(
                this.credentialDecryptor.Decrypt(credentials.UserName),
                this.credentialDecryptor.Decrypt(credentials.Password));
        }

        public NAMFoxConfiguration Configuration
        {
            get;
            private set;
        }

        public NeoseekerSection TestConfiguration { get; private set; }

        public string NAMFoxVersion
        {
            get { return this.Processor.GetString("getNAMFoxVersion", null); }
        }

        /// <summary>
        /// Gets or sets the browser against which this instance is targeted.
        /// </summary>
        public TargetBrowser TargetBrowser { get; set; }

        public NAMFoxToolbar Toolbar
        {
            get;
            private set;
        }

        public BrowserTabCollection Tabs
        {
            get;
            private set;
        }

        public void ClearLoginInformation()
        {
            this.commandProcessor.DoCommand("clearLoginInformation", null);
        }

        public void ClearSessionCookies()
        {
            this.commandProcessor.DoCommand("clearSessionCookies", null);
        }

        public User GetCurrentUser()
        {
            string[] data = this.commandProcessor.GetStringArray("getCurrentUser", null);

            return new User(data[0] == "null" ? null : data[0], bool.Parse(data[1]));
        }

        public string GetInnerHtml(string locator)
        {
            return this.commandProcessor.GetString("getInnerHtml", new string[] { locator });
        }

        public int GetLastPostIdOnPage()
        {
            var innerHtml = this.GetInnerHtml("//table[@id='threadlist']");
            var lastIndex = innerHtml.LastIndexOf("em_");
            if (lastIndex != -1)
            {
                lastIndex += 3;
                return Int32.Parse(innerHtml.Substring(lastIndex, innerHtml.IndexOf('"', lastIndex) - lastIndex));
            }

            throw new InvalidOperationException("Page does not have any posts.");
        }

        public void Login()
        {
            this.Login(false);
        }

        public void Login(bool autoLogin)
        {
            if (loggedIn)
            {
                return;
            }

            this.Login(this.login.UserName, this.login.Password, autoLogin);
            this.loggedIn = true;
        }

        public void LogOut()
        {
            if (!loggedIn)
            {
                return;
            }

            this.Processor.DoCommand("logOut", null);
            this.loggedIn = false;
        }

        public void PressEnter(string locator)
        {
            this.KeyPress(locator, "13");
        }

        public void SelectAll(string locator)
        {
            this.SelectRange(locator, 0, -1);
        }

        public void SelectRange(string locator, int start, int end)
        {
            this.commandProcessor.DoCommand("selectRange", new string[] { locator, "{ start: " + start + ", end: " + end + " }" });
        }

        public void SetDefaultMessageText(string text)
        {
            this.commandProcessor.DoCommand("setDefaultMessageText", new string[] { text });
        }

        public void SetMessageName(string key, string name)
        {
            this.commandProcessor.DoCommand("setMessageName", new string[] { key, name });
        }

        public void SetMessageText(string key, string text)
        {
            this.commandProcessor.DoCommand("setMessageText", new string[] { key, text });
        }

        /// <summary>
        /// Repeats an <paramref name="name"/> until a <paramref name="condition"/> is met
        /// or until 10 seconds has elapsed, whichever occurs first.
        /// </summary>
        /// <param name="action">The action to perform continuously.</param>
        /// <param name="condition">The condition that must be true before returning.</param>
        /// <returns>True if the action succeeded; otherwise, false.</returns>
        public bool RepeatUntil(Action action, Func<bool> condition)
        {
            return this.RepeatUntil(action, condition, 10000);
        }

        /// <summary>
        /// Repeats an <paramref name="name"/> until a <paramref name="condition"/> is met
        /// with a specific <paramref name="timeout"/>
        /// </summary>
        /// <param name="action">The action to perform continuously.</param>
        /// <param name="condition">The condition that must be true before returning.</param>
        /// <param name="timeout">The time, in milliseconds, that must elapse before aborting.</param>
        /// <returns>True if the action succeeded; otherwise, false.</returns>
        public bool RepeatUntil(Action action, Func<bool> condition, int timeout)
        {
            int iterations = 0;
            int maxIteration = timeout / 500;
            while (!condition() && ++iterations < maxIteration)
            {
                action();
                Thread.Sleep(500);
            }

            if (iterations == maxIteration)
            {
                return false;
            }

            return true;
        }

        private void Login(string userName, string password, bool autoLogin)
        {
            this.Processor.DoCommand("login", new string[] { autoLogin.ToString() + "|" + userName, password });
        }

        public void Initialize()
        {
            this.Processor.DoCommand("initialize", new string[] { this.login.UserName, this.login.Password });
        }

        /// <summary>
        /// Deletes the last post on the page the browser is currently pointed to.
        /// </summary>
        public void DeleteLastPostOnPage()
        {
            this.Click(XPathExpressions.DeleteLinkForLastPostOnPage);
            this.WaitForPageToLoad("30000");
            this.Click("//input[@value='CONFIRM DELETE']");
            this.WaitForPageToLoad("15000");
        }

        public void MakePost(int threadId, string post)
        {
            this.Open(Url.Forum("/index.php?fn=post_reply&t=" + threadId.ToString()));
            this.Type("post_textarea", post);
            this.Click("//input[@name='do' and @value='submit']");
            this.WaitForPageToLoad("30000");
        }
    }
}
